//
//  XLRuntimeTool.m
//  Demo
//
//  Created by tanqilong on 16/5/5.
//  Copyright © 2016年 Facebook. All rights reserved.
//

#import "XLRuntimeTool.h"
#import <objc/runtime.h>
#import <objc/message.h>

/**
 *  转化为C类型并发送实例消息
 *  @param exe   执行者
 *  @param type  数据类型
 *  @param param 参数
 */
#define XL_CTYPE_CONVERT_PERFORM_METHOD(exe,type,param) \
type result = [RCTConvert type:param];                  \
[exe retainArguments];                                  \
[exe setArgument:&result atIndex:i+2];

/**
 *  转化为OC类型并发送实例消息
 *  @param exe   执行者
 *  @param type  数据类型
 *  @param param 参数
 */
#define XL_OCTYPE_CONVERT_PERFORM_METHOD(exe,type,param) \
type* result = [RCTConvert type:param];                  \
[exe retainArguments];                                   \
[exe setArgument:&result atIndex:i+2];

//>>>类方法
/**
 *  转化为对应C参数执行类方法
 *  @param JsonParam js中的参数
 *  @param Type      要转的参数类型
 */
#define XL_C_PARAM_TRANSFORM(JsonParam,Type) \
  Type result = [RCTConvert Type:JsonParam];         \
  resultValue = ((void *(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,result);

/**
 *  转化为对应OC参数执行类方法
 *  @param Param     转化后的参数指针void*
 *  @param JsonParam js中的参数
 *  @param Type      要转的参数类型
 */
#define XL_OC_PARAM_TRANSFORM(JsonParam,Type) \
Type* result = [RCTConvert Type:JsonParam]; \
 resultValue = ((void*(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,result);

@implementation XLRuntimeTool

+(void)Object:(NSInteger)objPoint PerformSelector:(NSString *)selName JsonParams:(NSArray *)params
{
  if (objPoint == 0) {
    return;
  }
  id object = (__bridge id)((void *)objPoint);
  //消息签名
  SEL sel = NSSelectorFromString(selName);
  NSMethodSignature * sig = [[object class] instanceMethodSignatureForSelector:sel];
  if (sig == nil) {
    NSLog(@"==========没有%@的方法%@==============",[object class],selName);
    return;
  }
  //通过签名初始化
  NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:sig];
  [invocation setTarget:object];
  [invocation setSelector:sel];
  
  //设置参数
  [params enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    const char * argumentType = [sig getArgumentTypeAtIndex:idx + 2];
    switch (argumentType[0]) {
      case 'i':
      {
        NSInteger i = [obj integerValue];
        [invocation setArgument:&i atIndex:idx + 2];
        [invocation retainArguments];
      }
        break;
      case 'B':
      {
        BOOL b = [obj boolValue];
        [invocation setArgument:&b atIndex:idx + 2];
        [invocation retainArguments];
      }
        break;
      case 'f':
      case 'd':
      {
        double d = [obj doubleValue];
        [invocation setArgument:&d atIndex:idx + 2];
        [invocation retainArguments];
      }
        break;
      default:
        [invocation setArgument:&obj atIndex:idx + 2];
        [invocation retainArguments];
        break;
    }
  }];
  [invocation invoke];
}

+(id)Object:(NSInteger)objPoint PerformSelector:(NSString *)selName SpecialParams:(NSArray *)params ReturnType:(NSString *)returnType
{
  if (objPoint == 0) {
    return nil;
  }
  id object = (__bridge id)((void *)objPoint);
  //消息签名
  SEL sel = NSSelectorFromString(selName);
  NSMethodSignature * sig = [[object class] instanceMethodSignatureForSelector:sel];
  if (sig == nil) {
    NSLog(@"==========没有%@的方法%@==============",[object class],selName);
    return nil;
  }
  //通过签名初始化
  NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:sig];
  [invocation setTarget:object];
  [invocation setSelector:sel];
  //装入参数
  int i = 0;
  for (NSDictionary * paramDic in params) {
    NSString * paramType = [[paramDic allKeys]firstObject];
    id jsonParam = [[paramDic allValues]firstObject];
    //获取转化后的参数
    if([paramType isEqualToString:@"json"]){
      //json所支持的所有格式,但是int long 等都会同意转化为NSNumber,如要使用需单独指明
      id result = jsonParam;
      [invocation setArgument:&result atIndex:i+2];
    }else if([paramType isEqualToString:@"id"]){
      NSInteger pointInteger = [jsonParam integerValue];
      id result = (__bridge id)((void *)pointInteger);
      [invocation setArgument:&result atIndex:i+2];
    }else if([paramType isEqualToString:@"BOOL"]) {
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, BOOL, jsonParam);
    } else if([paramType isEqualToString:@"double"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, double, jsonParam);
    } else if([paramType isEqualToString:@"float"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, float, jsonParam);
    }else if([paramType isEqualToString:@"int"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, int, jsonParam);
    }else if([paramType isEqualToString:@"int64_t"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, int64_t, jsonParam);
    }else if([paramType isEqualToString:@"uint64_t"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, uint64_t, jsonParam);
    }else if([paramType isEqualToString:@"NSInteger"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, NSInteger, jsonParam);
    }else if([paramType isEqualToString:@"NSUInteger"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, NSUInteger, jsonParam);
    }else if([paramType isEqualToString:@"NSTimeInterval"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, NSTimeInterval, jsonParam);
    }else if([paramType isEqualToString:@"CGFloat"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, CGFloat, jsonParam);
    }else if([paramType isEqualToString:@"CGPoint"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, CGPoint, jsonParam);
    }else if([paramType isEqualToString:@"CGSize"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, CGSize, jsonParam);
    }else if([paramType isEqualToString:@"CGRect"]){
      XL_CTYPE_CONVERT_PERFORM_METHOD(invocation, CGRect, jsonParam);
    }else if([paramType isEqualToString:@"CGColor"]){
      CGColorRef cgcolor = [RCTConvert CGColor:jsonParam];
      [invocation setArgument:&cgcolor atIndex:i+2];
      [invocation retainArguments];
    }else if([paramType isEqualToString:@"NSDate"]){
      XL_OCTYPE_CONVERT_PERFORM_METHOD(invocation, NSDate, jsonParam);
    }else if([paramType isEqualToString:@"UIColor"]){
      XL_OCTYPE_CONVERT_PERFORM_METHOD(invocation, UIColor, jsonParam);
    }else if([paramType isEqualToString:@"NSTimeZone"]){
      XL_OCTYPE_CONVERT_PERFORM_METHOD(invocation, NSTimeZone, jsonParam);
    }else if([paramType isEqualToString:@"UIFont"]){
      XL_OCTYPE_CONVERT_PERFORM_METHOD(invocation, UIFont, jsonParam);
    }else if([paramType isEqualToString:@"NSURL"]){
      XL_OCTYPE_CONVERT_PERFORM_METHOD(invocation, NSURL, jsonParam);
    }else if([paramType isEqualToString:@"NSURLRequest"]){
      XL_OCTYPE_CONVERT_PERFORM_METHOD(invocation, NSURLRequest, jsonParam);
    }else {
      NSLog(@"调用了位置数据类型");
      id result = jsonParam;
      [invocation setArgument:&result atIndex:i+2];
      [invocation retainArguments];
    }
    i++;
  }
  //获取返回值
  
  //执行方法
  [invocation invoke];
  
  NSInteger length = [[invocation methodSignature] methodReturnLength];
  void * buffer = (void *)malloc(length);
  [invocation getReturnValue:buffer];
  //返回值
  return [self transformBuffer:buffer Type:returnType];
}

+(id)Class:(NSString *)className PerformSelector:(NSString *)selName JsonParams:(NSArray *)params ReturnType:(NSString *)returnType
{
  SEL sel = NSSelectorFromString(selName);
  Method method = class_getClassMethod(NSClassFromString(className), sel);
  //设置返回值
  void * resultValue;
  if (params.count == 1){
  NSDictionary * paramDic = params[0];
  id jsonParam = [[paramDic allValues] firstObject];
  NSString * paramType = [[paramDic allKeys] firstObject];
  if([paramType isEqualToString:@"json"]){
      //json所支持的所有格式,但是int long 等都会同意转化为NSNumber,如要使用需单独指明
    if (returnType == nil) {
       ((void(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,jsonParam);
    } else {
      resultValue = ((void *(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,jsonParam);
    }
    }else if([paramType isEqualToString:@"id"]){
      //id类型是由js页面传递来的id类指针
      NSInteger pointInteger = [jsonParam integerValue];
      id result = (__bridge id)((void *)pointInteger);
      resultValue = ((void *(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,result);
    }else if([paramType isEqualToString:@"BOOL"]) {
      XL_C_PARAM_TRANSFORM(jsonParam, BOOL)
    } else if([paramType isEqualToString:@"double"]){
      XL_C_PARAM_TRANSFORM(jsonParam, double)
    }else if([paramType isEqualToString:@"float"]){
      XL_C_PARAM_TRANSFORM(jsonParam, float)
    }else if([paramType isEqualToString:@"int"]){
      XL_C_PARAM_TRANSFORM(jsonParam, int)
    }else if([paramType isEqualToString:@"int64_t"]){
      XL_C_PARAM_TRANSFORM(jsonParam, int64_t)
    }else if([paramType isEqualToString:@"uint64_t"]){
      XL_C_PARAM_TRANSFORM(jsonParam, uint64_t)
    }else if([paramType isEqualToString:@"NSInteger"]){
      XL_C_PARAM_TRANSFORM(jsonParam, NSInteger)
    }else if([paramType isEqualToString:@"NSUInteger"]){
      XL_C_PARAM_TRANSFORM(jsonParam, NSUInteger)
    }else if([paramType isEqualToString:@"NSTimeInterval"]){
      XL_C_PARAM_TRANSFORM(jsonParam, NSTimeInterval)
    }else if([paramType isEqualToString:@"CGFloat"]){
      XL_C_PARAM_TRANSFORM(jsonParam, CGFloat)
    }else if([paramType isEqualToString:@"CGPoint"]){
      XL_C_PARAM_TRANSFORM(jsonParam, CGPoint)
    }else if([paramType isEqualToString:@"CGSize"]){
      XL_C_PARAM_TRANSFORM(jsonParam, int64_t)
    }else if([paramType isEqualToString:@"CGRect"]){
      XL_C_PARAM_TRANSFORM(jsonParam, int64_t)
    }else if([paramType isEqualToString:@"CGColor"]){
      CGColorRef cgcolor = [RCTConvert CGColor:jsonParam];
      resultValue = ((void *(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,cgcolor);
    }else if([paramType isEqualToString:@"NSDate"]){
      XL_OC_PARAM_TRANSFORM(jsonParam, NSDate)
    }else if([paramType isEqualToString:@"UIColor"]){
      XL_OC_PARAM_TRANSFORM(jsonParam, UIColor)
    }else if([paramType isEqualToString:@"NSTimeZone"]){
      XL_OC_PARAM_TRANSFORM(jsonParam, NSTimeZone)
    }else if([paramType isEqualToString:@"UIFont"]){
      XL_OC_PARAM_TRANSFORM(jsonParam, UIFont)
    }else if([paramType isEqualToString:@"NSURL"]){
      XL_OC_PARAM_TRANSFORM(jsonParam, NSURL)
    }else if([paramType isEqualToString:@"NSURLRequest"]){
      XL_OC_PARAM_TRANSFORM(jsonParam, NSURLRequest)
    }else {
      NSLog(@"调用了未知的数据类型");
      resultValue = ((void *(*)(id,Method,...))method_invoke)(NSClassFromString(className),method,jsonParam);
    }
  }else {
   resultValue = ((void*(*)(id,Method,...))method_invoke)(NSClassFromString(className),method);
  }
  //返回值
  return [self transformBuffer:resultValue Type:returnType];
}

//根据数据类型转化Buffer值
+(id)transformBuffer:(void *)buffer Type:(NSString *)type
{
  if(type == nil) {
    return nil;
  }
  id value;
  if([type isEqualToString:@"CGRect"]) {
    CGRect * rect = buffer;
    value = @{@"x":[NSNumber numberWithFloat:rect->origin.x],@"y":[NSNumber numberWithFloat:rect->origin.y],@"width":[NSNumber numberWithFloat:rect->size.width],@"height":[NSNumber numberWithFloat:rect->size.height]};
  } else if([type isEqualToString:@"id"]) {
    NSInteger point = (long)buffer;
    value = [NSNumber numberWithInteger:point];
  } else if ([type isEqualToString:@"CGPoint"]) {
    CGPoint * point = buffer;
    value = @{@"x":@(point->x),@"y":@(point->y)};
  } else if ([type isEqualToString:@"CGSize"]) {
    CGSize * size = buffer;
    value = @{@"width":@(size->width),@"height":@(size->height)};
  } else if ([type isEqualToString:@"UIColor"]) {
    UIColor * color = (__bridge id)buffer;
    const CGFloat * components = CGColorGetComponents(color.CGColor);
    int colorNumber = 0;
    int redColor = components[0] * 255;
    int greenColor = components[1] * 255;
    int blueColor = components[2] * 255;
    colorNumber = (redColor << 16)&0xff0000 + (greenColor << 8)&0xff00 + (blueColor);
    value = [NSNumber numberWithInt:colorNumber];
  } else if ([type isEqualToString:@"CGColor"]) {
    CGColorRef color = buffer;
    const CGFloat * components = CGColorGetComponents(color);
    int colorNumber = 0;
    int redColor = components[0] * 255;
    int greenColor = components[1] * 255;
    int blueColor = components[2] * 255;
    colorNumber = (redColor << 16)&0xff0000 + (greenColor << 8)&0xff00 + (blueColor);
    value = [NSNumber numberWithInt:colorNumber];
  }else if([type isEqualToString:@"NSNumber"]){
    double dNumber = *(double*)(buffer);
    value = [NSNumber numberWithDouble:dNumber];
  }else {
    value = (__bridge id)(buffer);
  }
  return value;
}

@end










